import datetime
import os
import shutil
import time

from loguru import logger

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

# 默认删除文件夹
DEFAULT_RM_DIR = "/Users/admin/Desktop/test2"
# 默认删除时间间隔
DEFAULT_TIMEDELTA = "2-D"


class DeleteFile:
    """删除文件操作类"""
    TIME_MAPPING = {'s': 1, 'm': 60, 'h': 3600, 'D': 86400, 'M': 30 * 86400, 'Y': 86400 * 365}
    TIME_MEANING = {'s': '秒', 'm': '分钟', 'h': '小时', 'D': '天', 'M': '月', 'Y': '年'}

    def __init__(self, is_scanner=False, rm_dir=None, timedelta=None, disk_rm=False, is_clean=False):
        """
        :param is_scanner: 是否从键盘输入 如果值为True，将会覆盖下面参数
        :param rm_dir: 删除操作的目录
        :param timedelta: 时间间隔
        :param disk_rm: 是否物理删除
        :param is_clean: 是否上一次删除文件
        """
        assert is_scanner or (rm_dir and timedelta), "DeleteFile init params are error"
        # 删除目录
        self.rm_dir = rm_dir
        self.time_num = None
        self.time_period = None
        # 偏移时间
        self.offset_time = None
        if timedelta is not None:
            self._input_timedelta(timedelta)
        # 是否物理删除
        self.disk_rm = disk_rm
        # 删除暂存文件夹 当不是物理删除，将会保存到当前目录
        self.mv_dir = None

        # 数据是否从键盘输入
        if is_scanner:
            self._scanner()
        # 初始化
        self._init(is_clean)

    def _init(self, is_clean=False):
        # 初始化日志
        logger.add(f"{BASE_DIR}/log/clean_file.log", rotation="10 MB", retention="3 months")
        logger.info("定时清理任务开始...")
        logger.info("开始执行清理文件夹（{}）下{}{}前的文件，{}物理删除。".format(
            self.rm_dir, self.time_num, self.TIME_MEANING[self.time_period], "" if self.disk_rm else "不是"
        ))
        # 保证目录以/结尾
        self.rm_dir = os.path.join(self.rm_dir, "")
        self.mv_dir = os.path.join(os.path.dirname(os.path.dirname(self.rm_dir)), "report_server_remove_files")
        # 清理上一次删除文件
        if is_clean and os.path.exists(self.mv_dir):
            shutil.rmtree(self.mv_dir)
        if not os.path.exists(self.mv_dir) and not self.disk_rm:
            os.makedirs(self.mv_dir)

    def _scanner(self):
        """输入相关参数"""
        num, period = self.parse_timedelta(DEFAULT_TIMEDELTA)
        print("**** 默认删除目录（{}）下{}{}前的文件 ****".format(
            DEFAULT_RM_DIR, num, self.TIME_MEANING.get(period)
        ))
        s = input("是否使用默认值(y/n)：")
        use_default_flag = self.parse_judge(s)
        if use_default_flag:
            self.rm_dir = DEFAULT_RM_DIR
            self._input_timedelta(DEFAULT_TIMEDELTA)
        else:
            self._input_rm_dir()
            self._input_timedelta()
        self._input_disk_rm()

        rm_meaning = "" if self.disk_rm else "不是"
        print("即将执行删除目录（{}）下{}{}前的文件，{}物理删除。".format(
            self.rm_dir, self.time_num, self.TIME_MEANING.get(self.time_period), rm_meaning
        ))
        is_run = input("是否执行(y/n)：")
        flag = self.parse_judge(is_run)
        if not flag:
            print("已退出操作..")
            exit(0)

    def _input_rm_dir(self):
        self.rm_dir = input("请输入要删除的文件目录：")
        if not os.path.exists(self.rm_dir):
            print("文件不存在...")
            self._input_rm_dir()
            return

    def _input_timedelta(self, timedelta=None):
        if timedelta is None:
            print("**** 时间间隔格式：数量-时间单位 如：1-s表示1秒 ****")
            print("**** 时间单位（s:秒 m:分钟 h:小时 D:天 M:月 Y:年）****")
            timedelta = input("请输入时间间隔：")
        num, period = self.parse_timedelta(timedelta)
        unit = self.TIME_MAPPING.get(period)
        self.offset_time = datetime.timedelta(seconds=-num * unit)
        self.time_num = num
        self.time_period = period
        return num, period

    def _input_disk_rm(self):
        print("**** 物理删除：不是物理删除将会将文件移动到与删除目录同目录下的remove_files文件夹 ****")
        judge = input("是否物理删除(y/n)：")
        self.disk_rm = self.parse_judge(judge)

    def parse_timedelta(self, timedelta):
        """解析时间间隔"""
        arr = timedelta.split('-')
        if len(arr) > 2 or len(arr) < 2:
            print("时间格式错误...")
            return self._input_timedelta()
        num, period = arr
        if period not in self.TIME_MAPPING:
            print("时间格式错误...")
            return self._input_timedelta()
        num = int(num)
        return num, period

    @staticmethod
    def parse_judge(judge):
        """解析y/n"""
        if judge.lower().startswith('y'):
            return True
        return False

    def delete_files_timedelta(self):
        """删除文件"""
        file_list = [self.rm_dir]
        # 获取当前时间
        today = datetime.datetime.now()
        # 获取想要的日期的时间,即前3天时间
        re_date = (today + self.offset_time)
        # 转换为时间戳
        re_date_unix = time.mktime(re_date.timetuple())

        while file_list:
            # 删除列表最后一个元素
            path = file_list.pop()
            for item in os.listdir(path):
                child_path = os.path.join(path, item)
                if os.path.isfile(child_path):
                    # 比较时间戳（时间间隔）
                    if os.path.getmtime(child_path) <= re_date_unix:
                        self._do_handle_file(path, child_path, item)
                else:
                    if not os.listdir(child_path):  # 判断目录是否为空
                        # 若目录为空，则删除
                        logger.info(f"删除空目录 {child_path}")
                        os.removedirs(child_path)
                    else:
                        # 为文件夹时,添加到列表中。再次循环。
                        file_list.append(child_path)
        logger.info("操作成功")

    def _do_handle_file(self, path, child_path, file_name):
        """执行处理文件操作"""
        time_local = time.localtime(os.path.getmtime(child_path))
        s = time.strftime('%Y-%m-%d %H:%M:%S', time_local)
        if self.disk_rm:
            os.remove(child_path)
            logger.info(f"删除文件（{s}）{child_path}")
            return
        # 非物理删除 将会将文件移动到与删除目录同目录下的remove_files文件夹
        sub_path = path.split(self.rm_dir)[1]
        mv_path = os.path.join(self.mv_dir, sub_path)
        if not os.path.exists(mv_path):
            os.makedirs(mv_path)
        dst_file_path = os.path.join(mv_path, file_name)
        shutil.copyfile(child_path, dst_file_path)
        os.remove(child_path)
        logger.info(f"移动文件（{file_name}）至目录 {mv_path}")


def main(is_scanner=False):
    try:
        DeleteFile(
            is_scanner=is_scanner,
            rm_dir=DEFAULT_RM_DIR,
            timedelta=DEFAULT_TIMEDELTA,
            disk_rm=False,
            is_clean=True
        ).delete_files_timedelta()
    except Exception as e:
        logger.error(f"\n程序异常退出, {e}")


if __name__ == '__main__':
    main()
